package server;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.StringTokenizer;

import server.bd.GestionDatos;
import server.gui.JFrameAdmin;
import util.SocketManager;


/**
 * Clase que da soporte al proceso principal del servidor. Consiste en un bucle infinito
 * que se dedicará a recibir nuevas conexiones y arrancar un hilo nuevo por cada conexión
 * 
 * @author josebama
 *
 */
public class Server {
	//Define el puerto en el que se estará escuchando
	private static final int PUERTO = 8891;
	
	/**Array de las sesiones que hay activas*/
	private ArrayList<Sesion> sesiones;
	/**Array con los hilos de las sesiones activas*/
//	private ArrayList<Thread> hilos;
	/**Número máximo de conexiones permitidas. Un valor negativo significa ilimitado*/
	private int maxCon;
	/**Valriable para saber si parar el servidor o no*/
	private boolean seguir;
	
	private JFrameAdmin jfA;
	
	/**
	 * Constructor: inicia las arrays y fija el valor de máximas conexiones a ilimitado
	 */
	public Server() {
		sesiones = new ArrayList<Sesion>();
//		hilos = new ArrayList<Thread>();
		maxCon = -1;
		seguir = true;
		jfA = new JFrameAdmin(this);
		jfA.setVisible(true);
	}
	
	public JFrameAdmin getJFrameAdmin() {
		return jfA;
	}
	
	/**
	 * Devuelve el número máximo de conexiones permitidas por el servidor y un valor inferior a 0 en caso de que no haya límite
	 * 
	 * @return Número máximo de conexiones permitidas
	 */
	public int getMaxCon() {
		return maxCon;
	}

	/**
	 * Define el número máximo de conexiones permitidas por el servidor. Para no poner ningún límite: introducir un valor inferior a 0
	 * 
	 * @param maxCon Número máximo de conexiones permitidas por el servidor
	 */
	public void setMaxCon(int maxCon) {
		this.maxCon = maxCon;
	}

	/**
	 * Devuelve una lista con las sesiones activas
	 * 
	 * @return Lista con las sesiones activas
	 */
	public ArrayList<Sesion> getSesiones() {
		return sesiones;
	}
	
	/**
	 * Recoge del archivo los datos de login
	 * 
	 * @return Array con todos los datos de login
	 */
	public ArrayList<Login> getLoginList() {
		ArrayList<Login> l = new ArrayList<Login>();
		try {
    		FileInputStream fileInputStream = new FileInputStream(GestionDatos.getLoginFile());
			InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
			BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
			String line = bufferedReader.readLine();
			while (line != null) {
				StringTokenizer st = new StringTokenizer(line, ";");
				String usuario = st.nextToken();
				String pass = st.nextToken();
				l.add(new Login(usuario,pass));
				line = bufferedReader.readLine();
			}
			inputStreamReader.close();
			fileInputStream.close();
		} catch (Exception e) {
	        e.printStackTrace();
	        throw new RuntimeException("Unexpected error, check the StackTrace");
		}
		return l;
	}
	
	/**
	 * Guarda en el archivo de login los datos de login recibidos
	 * 
	 * @param l Lista de logins a guardar en el archivo. Son todos los logins que habrá en el archivo, por lo que tienen que ir todos
	 */
	public void setLoginList(ArrayList<Login> l) {
		try {
    		BufferedWriter output = new BufferedWriter(new FileWriter(GestionDatos.getLoginFile()));
			Login i = l.get(0);
			if (i != null)
				output.write(i.getUsuario()+";"+i.getPass());
			l.remove(0);
			for (Login j : l) {
				output.write("\n"+j.getUsuario()+";"+j.getPass());
			}
			output.close();
		} catch (Exception e) {
	        e.printStackTrace();
	        throw new RuntimeException("Unexpected error, check the StackTrace");
		}
	}

	/**
	 * Ejecución del servidor. Aqui se producirá el bucle en el que el servidor se mantendrá a la espera de conexiones
	 * y luego gestionarlas, aceptarlas, crear nuevas sesiones etc
	 */
	public void arrancar() {
		try{
			//Crea un servidor de sockets escuchando en el puerto
            ServerSocket ss = new ServerSocket(PUERTO);
            System.out.println("Servidor iniciado");
            while(seguir) {
            	Socket s = ss.accept(); //Acepta la conexión
//            	s.setSoLinger(true, 10); //Establece un tiempo de desconexión para que no pierda el último mensaje
            	if (maxCon < 0 || sesiones.size() < maxCon) {
	            	//Arranca un nuevo hilo con una nueva sesión con el socket de la conexión que se ha aceptado
		            Sesion sesion = new Sesion(new SocketManager(s), this);
		            sesiones.add(sesion);
	            	Thread hilo = new Thread(sesion);
//		            hilos.add(hilo);
		            hilo.start(); 
            	} else {
            		SocketManager sm = new SocketManager(s);
            		sm.escribir("000 OK Conexion rechazada");
            		sm.cerrarStreams();
            		sm.cerrarSocket();
            	}
            }
        }catch(Exception e){ //En caso de que haya algún error, mostrarlo
            System.err.println("main: " + e);
            e.printStackTrace();
        }
	}
	
	/**
	 * Método para parar la ejecución del servidor
	 */
	public void parar() {
		for (Sesion sesion : sesiones) {
			cerrarSesion(sesion);
		}
		seguir = false;
	}
	
	/**
	 * Método para cerrar una sesión (expulsar a un cliente del servidor)
	 * 
	 * @param sesion La sesión que se quiere cerrar
	 */
	public void cerrarSesion(Sesion sesion) {
		int pos = sesiones.indexOf(sesion);
		sesion.end();
//		try {
//			hilos.get(pos).join();
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}
//		hilos.remove(pos);
		sesiones.remove(pos);
		jfA.actualizar();
	}
	
	/**
	 * 
	 * MAIN
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		Server server = new Server();
		server.arrancar();
		JFrameAdmin jfA = server.getJFrameAdmin();
		jfA.setVisible(true);
	}
}
